package com.example.async.service;

import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;


@Slf4j
@Service
public class AsyncService {



    @Async
    public void noReturnMethod() {
        log.info("调用无返回值方法");
    }

    @Async
    public Future<String> returnFuture() {
        log.info("调用异步返回方法");
        return CompletableFuture.completedFuture("done");
    }

    @Async
    public CompletableFuture<String> returnCompletableFuture() {
        log.info("调用异步返回CompletableFuture方法");
        return CompletableFuture.completedFuture("done");
    }


    public void someOtherMethod() {
        log.info("spring aop代理失效，类内调用");
        this.noReturnMethod();
        this.returnFuture();
        this.returnCompletableFuture();
    }

    @Lazy
    @Autowired
    private AsyncService selfProxy;

    public void someOtherMethod2() {
        log.info("注入类自身的代理调用异步方法，异步调用成功");
        log.info("注入自身代理，要解决循环依赖问题，需要使用@Lazy注解延迟加载，需要时注入");
        selfProxy.noReturnMethod();
        selfProxy.returnFuture();
        selfProxy.returnCompletableFuture();
    }


    // 解决代理对象失效问题： 1. 类拆分、2. 注入自身代理对象、3. 使用AopContext获取当前代理对象 (不能切换线程)
    public void someOtherMethod3() {
        // 异步调用，会切换线程， AopContext.currentProxy() 暴露的代理对象和当前线程绑定
        AsyncService asyncService = (AsyncService) AopContext.currentProxy();
        log.info("someOtherMethod3 AopContext获取当前代理对象，异步调用成功");
        asyncService.noReturnMethod();
        asyncService.returnFuture();
        asyncService.returnCompletableFuture();
    }


    // 异步方法中不要同时处理事务， @Scheduled 同样也不要处理事务， 会发生 Thread Context
    // 导致事务失效，一定要拆分出去， 也更符合单一职责、关注点分离
    // @Cacheable 相关注解不同、没有发生  Thread Context 切换， 不会导致事务失效
}